
// 17.21 in 802.1w-2001

#include "802_1w_2001_procedures.h"
#include "bridge.h"
#include "port.h"
#include "log.h"

#include "assert.h"

enum
{
	UNDEFINED,
	DISABLED,
	AGED,
	UPDATE,
	SUPERIOR,
	REPEAT,
	AGREEMENT,
	CURRENT,
	RECEIVE,
};

// ============================================================================

const char* PortInformation_802_1w_2001_GetStateName (SM_STATE state)
{
	switch (state)
	{
		case DISABLED:	return "DISABLED";
		case AGED:		return "AGED";
		case UPDATE:	return "UPDATE";
		case SUPERIOR:	return "SUPERIOR";
		case REPEAT:	return "REPEAT";
		case AGREEMENT:	return "AGREEMENT";
		case CURRENT:	return "CURRENT";
		case RECEIVE:	return "RECEIVE";
		default:		return "(undefined)";
	}
}

// ============================================================================

SM_STATE PortInformation_802_1w_2001_CheckConditions (BRIDGE* bridge, int givenPort, int givenTree, SM_STATE state)
{
	assert (givenPort != -1);
	assert (givenTree != -1);
	
	PORT* port = bridge->ports [givenPort];
	PORT_TREE* portTree = port->trees [givenTree];
	
	// ------------------------------------------------------------------------
	// Check global conditions.
	
	if ((!port->portEnabled && (portTree->infoIs != INFO_IS_DISABLED)) || bridge->BEGIN)
	{
		if (state == DISABLED)
		{
			// The entry block for this state has been executed already.
			return 0;
		}
		
		return DISABLED;
	}
	
	// ------------------------------------------------------------------------
	// Check exit conditions from each state.
	
	if (state == DISABLED)
	{
		if (port->rcvdBpdu)
			return DISABLED;
		
		if (port->portEnabled)
			return AGED;
		
		if (portTree->updtInfo)
			return DISABLED;
		
		return 0;
	}
	
	if (state == AGED)
	{
		if (portTree->selected && portTree->updtInfo)
			return UPDATE;
		
		return 0;
	}
	
	if (state == UPDATE)
		return CURRENT;
	
	if (state == CURRENT)
	{
		if (portTree->selected && portTree->updtInfo)
			return UPDATE;
		
		if ((portTree->infoIs == INFO_IS_RECEIVED) && (portTree->rcvdInfoWhile == 0) && !portTree->updtInfo && !port->rcvdBpdu)
			return AGED;
		
		if (port->rcvdBpdu && !portTree->updtInfo)
			return RECEIVE;
		
		return 0;
	}
	
	if (state == RECEIVE)
	{
		if (portTree->rcvdInfo == RCVD_INFO_SUPERIOR_DESIGNATED)
			return SUPERIOR;
		
		if (portTree->rcvdInfo == RCVD_INFO_REPEATED_DESIGNATED)
			return REPEAT;
		
		if (portTree->rcvdInfo == RCVD_INFO_CONFIRMED_ROOT_MSG)
			return AGREEMENT;
		
		if (portTree->rcvdInfo == RCVD_INFO_OTHER)
			return CURRENT;
		
		return 0;
	}
	
	if (state == SUPERIOR)
		return CURRENT;
	
	if (state == REPEAT)
		return CURRENT;
	
	if (state == AGREEMENT)
		return CURRENT;
		
	assert (false);
	return 0;
}

// ============================================================================

void PortInformation_802_1w_2001_InitState (BRIDGE* bridge, int givenPort, int givenTree, SM_STATE state)
{
	assert (givenPort != -1);
	assert (givenTree != -1);
	
	PORT* port = bridge->ports [givenPort];
	PORT_TREE* portTree = port->trees [givenTree];
	
	if (state == DISABLED)
	{
		port->rcvdBpdu = port->rcvdRSTP = port->rcvdSTP = false;
		portTree->portPriority = portTree->designatedPriority;
		portTree->portTimes = portTree->designatedTimes;
		portTree->updtInfo = portTree->proposing = false;
		portTree->agreed = portTree->proposed = false;
		portTree->rcvdInfoWhile = 0; portTree->infoIs = INFO_IS_DISABLED;
		portTree->reselect = true; portTree->selected = false;
	}
	else if (state == AGED)
	{
		portTree->infoIs = INFO_IS_AGED;
		portTree->reselect = true; portTree->selected = false;
	}
	else if (state == UPDATE)
	{
		portTree->portPriority = portTree->designatedPriority;
		portTree->portTimes = portTree->designatedTimes;
		portTree->updtInfo = 0;
		portTree->agreed = portTree->synced = 0;
		portTree->proposed = portTree->proposing = 0;
		portTree->infoIs = INFO_IS_MINE; port->newInfo = 1;
	}
	else if (state == CURRENT)
	{
	}
	else if (state == RECEIVE)
	{
		portTree->rcvdInfo = rcvBpdu_802_1w_2001 (bridge, givenPort); updtBPDUVersion_802_1w_2001 (bridge, givenPort);
		setTcFlags_802_1w_2001 (bridge, givenPort);
		port->rcvdBpdu = 0;
	}
	else if (state == SUPERIOR)
	{
		portTree->portPriority = portTree->msgPriority; portTree->portTimes = portTree->msgTimes;
		updtRcvdInfoWhile_802_1w_2001 (bridge, givenPort);
		portTree->agreed = portTree->proposing = portTree->synced = false;
		portTree->proposed = recordProposed_802_1w_2001 (bridge, givenPort); portTree->infoIs = INFO_IS_RECEIVED;
		portTree->reselect = true; portTree->selected = false;
	}
	else if (state == REPEAT)
	{
		portTree->proposed = recordProposed_802_1w_2001 (bridge, givenPort);
		updtRcvdInfoWhile_802_1w_2001 (bridge, givenPort);
	}
	else if (state == AGREEMENT)
	{
		portTree->agreed = 1; portTree->proposing = 0;
	}
	else
		assert (false);
}
